package com.gaoding.shared.dict.provider;

import com.gaoding.commons.exception.NotFoundException;
import com.gaoding.commons.exception.ServiceException;
import com.gaoding.commons.exception.ValidateException;
import com.gaoding.grpc.common.protobuf.Common;
import com.gaoding.orm.jpa.search.BaseSearchable;
import com.gaoding.orm.jpa.search.SearchOperator;
import com.gaoding.shared.dict.common.constant.DictMessageConstant;
import com.gaoding.shared.dict.entity.DictServiceLanguage;
import com.gaoding.shared.dict.entity.DictTextContent;
import com.gaoding.shared.dict.enums.DictServiceErrorCode;
import com.gaoding.shared.dict.repository.DictServiceLanguageRepository;
import com.gaoding.shared.dict.repository.DictTextContentRepository;
import com.gaoding.shared.dict.rpc.service.CreateDictTextContentRequest;
import com.gaoding.shared.dict.rpc.service.DictTextContentInfo;
import com.gaoding.shared.dict.rpc.service.DictTextContentReply;
import com.gaoding.shared.dict.rpc.service.GdGrpcDictTextContentService;
import com.gaoding.shared.dict.rpc.service.GetTextContentListRequest;
import com.gaoding.shared.dict.rpc.service.QueryDictTextContentRequest;
import com.gaoding.shared.dict.rpc.service.QueryListKeyDictTextContentRequest;
import com.gaoding.shared.dict.rpc.service.QueryOneDictTextContentRequest;
import com.gaoding.shared.dict.rpc.service.UpdateDictTextContentRequest;
import com.gaoding.shared.dict.service.DictServiceLanguageService;
import com.gaoding.shared.dict.service.DictTextContentService;
import com.google.protobuf.BoolValue;
import com.google.protobuf.Int64Value;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.devh.boot.grpc.server.service.GrpcService;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.data.domain.Page;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * 字典内容配置 grpc服务实现
 *
 * @author mobai
 * @since 2021/1/29 10:47
 */
@GrpcService
@Slf4j
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class DictTextContentServiceGrpcImpl extends GdGrpcDictTextContentService {

    private final DictTextContentService dictTextContentService;


    private final DictServiceLanguageService dictServiceLanguageService;

    /**
     * 全量\增量 获取
     *
     * @param request 请求入参
     * @return com.gaoding.shared.dict.rpc.service.DictTextContentReply
     * @since 2021/1/29 10:49 上午
     */
    @Override
    public DictTextContentReply list(QueryDictTextContentRequest request) {
        String serviceCode = request.getServiceCode();
        String languageCode = StringUtils.isNotBlank(request.getLanguageCode()) ? request.getLanguageCode() : DictMessageConstant.DEFAULT_LANGUAGE_CODE;
        long version = request.getVersion();
        List<DictTextContent> resultList = Collections.emptyList();
        List<DictTextContentInfo> result = Collections.emptyList();
        BaseSearchable searchable = BaseSearchable.newSearchable();
        if (StringUtils.isBlank(serviceCode)) {
            throw new ValidateException("服务编码不得为空");
        }
        DictServiceLanguage config = findServiceLanguage(serviceCode, languageCode);
        // 为0 表示全量更新， 则version不作为查询条件,且只查status为1的数据
        if (version == 0) {
            resultList = dictTextContentService.findAll(searchable.addSearchFilter("serviceCode", SearchOperator.eq, serviceCode).addSearchFilter("languageCode", SearchOperator.eq, languageCode)).getContent();
        } else {
            //不为0 则比较服务语言配置表中的version，不一致则为增量更新
            if (config == null) {
                throw new ServiceException(DictServiceErrorCode.SERVICE_LANGUAGE_CONFIG_NOT_EXIST.getCode(), "增量更新出错，原因是服务语言没有进行配置");
            }
            if (config.getVersion() != version) {
                DictTextContentRepository repository = (DictTextContentRepository) dictTextContentService.getRepository();
                resultList = repository.listWithoutLogicFlag(serviceCode, languageCode, version);
            }

        }
        version = config.getVersion();
        result = resultList.stream().map(this::transfer).collect(Collectors.toList());
        return DictTextContentReply.newBuilder().addAllData(result).setVersion(version).build();
    }

    /**
     * 根据服务编码、语言编码、key查询单个字典值
     *
     * @param request 请求入参
     * @return com.gaoding.shared.dict.rpc.service.DictTextContentInfo
     * @since 2021/1/29 10:50 上午
     */

    @Override
    public DictTextContentInfo one(QueryOneDictTextContentRequest request) {
        if (StringUtils.isAnyBlank(request.getServiceCode(), request.getLanguageCode(), request.getTextKey())) {
            throw new ValidateException("服务编码、语言编码和文本key不得为空");
        }
        DictTextContentRepository repository = (DictTextContentRepository) dictTextContentService.getRepository();
        List<DictTextContent> result = repository.findAllByServiceCodeAndLanguageCodeAndTextKey(request.getServiceCode(), request.getLanguageCode(), request.getTextKey());
        if (CollectionUtils.isNotEmpty(result)) {
            return transfer(result.get(0));
        }
        return null;
    }


    /**
     * 根据语言和key获取存在的字典列表值
     *
     * @param request 请求入参
     * @return com.gaoding.shared.dict.rpc.service.DictTextContentReply
     * @since 2021/1/29 10:54 上午
     */

    @Override
    public DictTextContentReply listKey(QueryListKeyDictTextContentRequest request) {
        if (StringUtils.isAnyBlank(request.getLanguageCode(), request.getTextKey())) {
            throw new ValidateException("语言编码和文本key不得为空");
        }
        Page<DictTextContent> textContents = dictTextContentService.findAll(BaseSearchable.newSearchable().addSearchFilter("languageCode", SearchOperator.eq, request.getLanguageCode()).addSearchFilter("textKey", SearchOperator.eq, request.getTextKey()));
        return DictTextContentReply.newBuilder().addAllData(textContents.getContent().stream().map(this::transfer).collect(Collectors.toList())).build();
    }

    /**
     * 字典内容新增(单个key)
     *
     * @param request 请求入参
     * @return com.google.protobuf.BoolValue
     * @since 2021/1/29 10:55 上午
     */

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Int64Value create(CreateDictTextContentRequest request) {
        String languageCode = request.getLanguageCode();
        String textContent = request.getTextContent();
        String serviceCode = request.getServiceCode();
        String textKey = request.getTextKey();
        if ((!StringUtils.isAnyBlank(languageCode, textKey, serviceCode, textContent)) && request.hasTextType()) {
            //根据服务语言编码查询是否配置过,因为涉及到version更新，所以未配置不允许新增
            DictServiceLanguage serviceLanguage = findServiceLanguage(serviceCode, languageCode);
            //判断是否存在唯一索引相同数据
            DictTextContentRepository repository = (DictTextContentRepository) dictTextContentService.getRepository();
            List<DictTextContent> textContents = repository.findAllByServiceCodeAndLanguageCodeAndTextKey(serviceCode, languageCode, textKey);
            if (CollectionUtils.isNotEmpty(textContents)) {
                throw new ServiceException(DictServiceErrorCode.TEXT_CONTENT_KEY_EXIST);
            }
            long version = System.currentTimeMillis();
            DictTextContent saveEntity = DictTextContent.builder().status(DictMessageConstant.DEFAULT_STATUS).serviceCode(serviceCode).languageCode(languageCode).textType(request.getTextType().getValue())
                    .textContent(textContent).version(version).textKey(textKey).sort(0).build();
            serviceLanguage.setVersion(version);
            try {
                dictServiceLanguageService.save(serviceLanguage);
                return Int64Value.of(dictTextContentService.save(saveEntity).getId());
            } catch (Exception e) {
                log.error("新增字典内容配置出错，原因:{}", e.getMessage());
                //这里必须抛出异常,否则spring事务会认为提交了一个read only的内部事务
                throw new ServiceException(DictServiceErrorCode.TEXT_CONTENT_CREATE_ERROR);
            }
        } else {
            throw new ValidateException("存在为空参数");
        }

    }


    /**
     * 根据id修改字典内容值(修改字典类型和字典值)
     *
     * @param request 请求入参
     * @return com.google.protobuf.BoolValue
     * @since 2021/1/29 10:55 上午
     */

    @Override
    @Transactional(rollbackFor = Exception.class)
    public BoolValue update(UpdateDictTextContentRequest request) {
        String textContent = request.getTextContent();
        if (StringUtils.isBlank(textContent) || !request.hasTextType() || !request.hasId()) {
            throw new ValidateException("文本类型、文本内容和id不得为空");
        }
        long version = System.currentTimeMillis();
        DictTextContent dictTextContent = Optional.<DictTextContent>ofNullable(dictTextContentService.findOne(request.getId().getValue())).orElseThrow(() -> new ServiceException(DictServiceErrorCode.TEXT_CONTENT_NOT_EXIST));
        dictTextContent.setTextContent(textContent);
        dictTextContent.setVersion(version);
        dictTextContent.setTextType(request.getTextType().getValue());
        DictServiceLanguage serviceLanguage = findServiceLanguage(dictTextContent.getServiceCode(), dictTextContent.getLanguageCode());
        serviceLanguage.setVersion(version);
        try {
            dictTextContentService.save(dictTextContent);
            dictServiceLanguageService.save(serviceLanguage);
            return BoolValue.of(true);
        } catch (Exception e) {
            log.error("更新字典内容配置出错,原因:{}", e.getMessage());
            //这里必须抛出异常,否则spring事务会认为提交了一个read only的内部事务
            throw new ServiceException(DictServiceErrorCode.TEXT_CONTENT_UPDATE_ERROR);
        }
    }


    /**
     * 根据id 逻辑删除
     *
     * @param request 主键id
     * @return com.google.protobuf.BoolValue
     * @since 2021/1/29 10:56 上午
     */

    @Override
    public BoolValue delete(Int64Value request) {
        try {
            dictTextContentService.delete(request.getValue());
        } catch (EmptyResultDataAccessException e) {
            log.error("要删除的字典内容不存在");
            throw new NotFoundException("要删除的字典内容不存在");
        }
        return BoolValue.of(true);
    }

    @Override
    public DictTextContentReply getList(GetTextContentListRequest request) {
        DictTextContentReply reply = DictTextContentReply.newBuilder().addAllData(Collections.emptyList()).build();
        BaseSearchable searchable = BaseSearchable.newSearchable();
        searchable.setPage(request.getPage().getPageNum(), request.getPage().getPageSize());
        if (StringUtils.isNotBlank(request.getServiceCode())) {
            searchable.addSearchFilter("serviceCode", SearchOperator.like, request.getServiceCode());
        }
        if (StringUtils.isNotBlank(request.getLanguageCode())){
            searchable.addSearchFilter("languageCode", SearchOperator.like, request.getLanguageCode());
        }

        if (StringUtils.isNotBlank(request.getTextKey())) {
            searchable.addSearchFilter("textKey", SearchOperator.like, request.getTextKey());
        }
        if (request.hasTextType()){
            searchable.addSearchFilter("textType", SearchOperator.eq, request.getTextType().getValue());
        }
        Common.Page queryPage = request.getPage();
        searchable.setPage(queryPage.getPageNum(), queryPage.getPageSize());
        Page<DictTextContent> textContents = dictTextContentService.findAll(searchable);
        if (!CollectionUtils.isEmpty(textContents.getContent())) {
            List<DictTextContentInfo> serviceListInfos = textContents.getContent().stream().map(this::transfer).collect(Collectors.toList());
            reply = DictTextContentReply.newBuilder().addAllData(serviceListInfos).build();
        }
        return reply;
    }

    /**
     * 实体转dto
     *
     * @param entity 实体
     * @return com.gaoding.shared.dict.rpc.service.DictTextContentInfo
     * @since 2021/2/1 9:51 上午
     */

    private DictTextContentInfo transfer(DictTextContent entity) {
        if (entity != null && entity.getId() != null) {
            return DictTextContentInfo.newBuilder().setServiceCode(entity.getServiceCode()).setTextContent(entity.getTextContent()).setLanguageCode(entity.getLanguageCode())
                    .setSort(entity.getSort()).setId(entity.getId()).setVersion(entity.getVersion()).setStatus(entity.getStatus()).setTextType(entity.getTextType()).setTextKey(entity.getTextKey()).build();
        }
        return null;

    }

    /**
     * 根据服务编码和语言编码查询服务语言配置记录，找不到抛出异常
     *
     * @param serviceCode  服务编码
     * @param languageCode 语言编码
     * @return com.gaoding.shared.dict.entity.DictServiceLanguage
     * @since 2021/2/1 9:51 上午
     */

    public DictServiceLanguage findServiceLanguage(String serviceCode, String languageCode) {
        DictServiceLanguageRepository dictServiceLanguageRepository = (DictServiceLanguageRepository) dictServiceLanguageService.getRepository();
        List<DictServiceLanguage> dictServiceLanguageList = dictServiceLanguageRepository.findByServiceCodeAndLanguageCode(serviceCode, languageCode);
        if (CollectionUtils.isEmpty(dictServiceLanguageList)) {
            throw new ServiceException(DictServiceErrorCode.TEXT_CONTENT_SERVICE_LANGUAGE_NOT_CONFIGURED);
        }

        return dictServiceLanguageList.get(0);
    }
}
